﻿// Open Source: MIT License (http://www.opensource.org/licenses/mit-license.php)
// Project: YoProgramo.NET (http://code.google.com/p/yoprogramo/)
// Autor: Fabian Figueredo (info@fabianfigueredo.com.ar)
// Blog: blog.yoprogramo.net

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Web;
using System.Web.Mvc;
using Microsoft.Practices.ServiceLocation;
using YoProgramo.Domain.BusinessLogic.Extensions;
using YoProgramo.Domain.Core;
using YoProgramo.Domain.Core.Interfaces;

namespace YoProgramo.Domain.BusinessLogic.Attributes
{
    public class YpAuthorizeAttribute : AuthorizeAttribute {

        private string[] _rolesSplit = new string[0];
        private string[] _usersSplit = new string[0];
        private Roles[] _roles;

        public Roles[] Roles
        {
            get
            {
                return _roles;
            }
            set
            {
                _roles = value;
            }
        }        

        // This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
        protected override bool AuthorizeCore(HttpContextBase httpContext) {
            if (httpContext == null) {
                throw new ArgumentNullException("httpContext");
            }

            IUserRepository userRepository =
                ServiceLocator.Current.GetInstance<IUserRepository>();

            IIdentity userIdentity = httpContext.User.Identity;
            if (!userIdentity.IsAuthenticated) {
                return false;
            }

            IUser useryp = userRepository.FindByUserName(userIdentity.Name);

            _usersSplit = SplitString(Users);

            if (_usersSplit.Length > 0 && !_usersSplit.Contains(useryp.UserName, StringComparer.OrdinalIgnoreCase)) {
                return false;
            }

            if (!_roles.Any(useryp.IsInRole)) {
                return false;
            }

            return true;
        }


        public override void OnAuthorization(AuthorizationContext filterContext) {
            if (filterContext == null) {
                throw new ArgumentNullException("filterContext");
            }

            if (AuthorizeCore(filterContext.HttpContext)) {
                // ** IMPORTANT **
                // Since we're performing authorization at the action level, the authorization code runs
                // after the output caching module. In the worst case this could allow an authorized user
                // to cause the page to be cached, then an unauthorized user would later be served the
                // cached page. We work around this by telling proxies not to cache the sensitive page,
                // then we hook our custom authorization code into the caching mechanism so that we have
                // the final say on whether a page should be served from the cache.

                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
            } else {
                // auth failed, redirect to login page
                filterContext.Result = new HttpUnauthorizedResult();
            }
        }


        private string[] SplitString(string original) {
            if (String.IsNullOrEmpty(original)) {
                return new string[0];
            }

            var split = from piece in original.Split(',')
                        let trimmed = piece.Trim()
                        where !String.IsNullOrEmpty(trimmed)
                        select trimmed;
            return split.ToArray();
        }

        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) {
            validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
        }

    }
}